package test.threads.queue.unstressed.impl;

import java.util.concurrent.atomic.AtomicLongFieldUpdater;

import test.threads.queue.unstressed.IBoundedQueueFactory;
import test.threads.queue.unstressed.IQueue;

import static com.google.common.base.Preconditions.checkArgument;

/**
 * Упрощенный вариант, без наследования -- чтобы постить в блог
 *
 * @author cheremin
 * @since 27.09.11,  14:58
 */
public class CABSESDLazySetUnrolledQueue<T> implements IQueue<T> {
    private static final boolean USE_BITMASK = true;
    //======================================================
    private static final AtomicLongFieldUpdater<CABSESDLazySetUnrolledQueue> tailUpdater = AtomicLongFieldUpdater.newUpdater( CABSESDLazySetUnrolledQueue.class, "tailCursor" );
    private static final AtomicLongFieldUpdater<CABSESDLazySetUnrolledQueue> headUpdater = AtomicLongFieldUpdater.newUpdater( CABSESDLazySetUnrolledQueue.class, "headCursor" );
    //======================================================

    /* Elements range: [headCursor, tailCursor)
    *
    * (tailCursor - headCursor) == elements count
    *
    * 0 <= (tailCursor - headCursor) <= length  => state invariant
    *
    * tailCursor - headCursor == length         => queue is full
    * tailCursor - headCursor == 0              => queue is empty
    *
    * (headCursor % size ) is the index of first item in queue
    * (tailCursor % size ) is the index of _cell_ for _next last item_
    */

    private final int length;
    private volatile long headCursor = 0;
    private volatile long tailCursor = 0;

    private final T[] elements;


    public CABSESDLazySetUnrolledQueue( final int length ) {
        checkArgument( length > 0, "length(%s) must be >0", length );
        if ( USE_BITMASK ) {
            checkArgument( Integer.bitCount( length ) == 1, "length(%s) must be a power of 2", length );
        }
        this.length = length;
        elements = ( T[] ) new Object[length];
    }

    private long nextTail() {
        final long tail = tailCursor;
        waitWhileNotFull( tail );

        assert ( tail == tailCursor ) : "tail=" + tail + " <> tailCursor=" + tailCursor + " -- seems like multi-threaded enqueue() detected!";
        return tail + 1;
    }

    private long nextHead() {
        final long head = headCursor;
        waitWhileNotEmpty( head );
        assert ( head == headCursor ) : "head=" + head + " <> headCursor=" + headCursor + " -- seems like multi-threaded dequeue() detected!";
        return head + 1;
    }


    private void publishTail( final long newTail ) {
        assert ( newTail >= headCursor ) : "newTail(" + newTail + ") < headCursor(" + headCursor + ")";
        tailUpdater.lazySet( this, newTail );
    }

    private void publishHead( final long newHead ) {
        assert ( newHead <= tailCursor ) : "newHead(" + newHead + ") > tailCursor(" + tailCursor + ")";
        headUpdater.lazySet( this, newHead );
    }


    private void waitWhileNotFull( final long tail ) {
        //spin-wait: "while not full"
        final long target = tail - length;
        while ( target == headCursor ) {
//            Thread.yield();
        }
    }

    private void waitWhileNotEmpty( final long head ) {
        //spin-wait: "while not empty"
        while ( head == tailCursor ) {
//            Thread.yield();
        }
    }

    public void enqueue( final T item ) {
        final long newTail = nextTail();

        final int index = index( newTail );
        elements[index] = item;

        publishTail( newTail );
    }

    public T dequeue() {
        final long newHead = nextHead();

        final int index = index( newHead );
        final T item = elements[index];
        elements[index] = null;//for GC

        publishHead( newHead );
        return item;
    }

    private int index( final long sequence ) {
        if ( USE_BITMASK ) {
            return ( int ) ( ( sequence - 1 ) & ( length - 1 ) );
        } else {
            return ( int ) ( ( sequence - 1 ) % length );
        }
    }

    public int size() {
        return ( int ) ( tailCursor - headCursor );
    }

    @SuppressWarnings( "unchecked" )
    public static <T> IBoundedQueueFactory<T, CABSESDLazySetUnrolledQueue<T>> factory() {
        return FACTORY;
    }

    public static final IBoundedQueueFactory FACTORY = new IBoundedQueueFactory<Object, CABSESDLazySetUnrolledQueue<Object>>() {
        @Override
        public CABSESDLazySetUnrolledQueue<Object> create( final int size ) {
            return new CABSESDLazySetUnrolledQueue<Object>( size );
        }

        @Override
        public String toString() {
            return "SESDLazySetUnrolledQueue[" + ( USE_BITMASK ? "&" : "%" ) + "]";
        }
    };
}
